백준 16946번

벽 부수고 이동하기 4

Posted by ChoiCube84 on January 29, 2024 · 9 mins read

백준 16946번

오늘 풀어본 문제는 백준의 16946번 문제1이다. 문제 풀이에 사용한 언어는 C++ 이다.

solved.ac 기준 CLASS

CLASS 5

문제 정보

이 문제의 내용과 조건은 다음과 같다.

문제

$N \times M$ 의 행렬로 표현되는 맵이 있다. 맵에서 $0$ 은 이동할 수 있는 곳을 나타내고, $1$ 은 이동할 수 없는 벽이 있는 곳을 나타낸다. 한 칸에서 다른 칸으로 이동하려면, 두 칸이 인접해야 한다. 두 칸이 변을 공유할 때, 인접하다고 한다.

각각의 벽에 대해서 다음을 구해보려고 한다.

  • 벽을 부수고 이동할 수 있는 곳으로 변경한다.
  • 그 위치에서 이동할 수 있는 칸의 개수를 세어본다.

한 칸에서 이동할 수 있는 칸은 상하좌우로 인접한 칸이다.

입력

첫째 줄에 $N$ $(1 \le N \le 1,000)$, $M$ $(1 \le M \le 1,000)$ 이 주어진다. 다음 N개의 줄에 M개의 숫자로 맵이 주어진다.

출력

맵의 형태로 정답을 출력한다. 원래 빈 칸인 곳은 $0$ 을 출력하고, 벽인 곳은 이동할 수 있는 칸의 개수를 $10$ 으로 나눈 나머지를 출력한다.

풀이과정

1번째 시도

문제를 해결하기 위해 사용한 방식은 다음과 같다.

  1. 모든 빈 공간에서 DFS 를 진행하여 각 구역별 지점의 개수, 각 지점에 속한 구역의 번호를 알아낸다.

  2. 각 벽에서 주위의 빈 공간들의 구역의 번호를 알아내어 해당 구역들에 속한 지점의 개수를 모두 더한 뒤 $10$ 으로 나눈 나머지를 구한다.

코드는 다음과 같이 작성하였다.

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using ull = unsigned long long;
using pll = pair<ll, ll>;

int dy[4] = {-1, 1, 0, 0};
int dx[4] = {0, 0, -1, 1};

bool isInRange(const pair<int, int>& pos, int N, int M);

int main(void) {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int N, M;
    cin >> N >> M;

    vector<vector<bool>> grid(N, vector<bool>(M, false));
    vector<vector<bool>> visited(N, vector<bool>(M, false));
    vector<vector<int>> setNum(N, vector<int>(M, 0));
    vector<int> pointCounts;

    stack<pair<pair<int, int>, int>> s;

    for (int i=0; i<N; i++) {
        string input;
        cin >> input;

        for (int j=0; j<M; j++) {
            if (input[j] == '1') {
                grid[i][j] = true;
            }
            else {
                s.emplace(make_pair(i, j), -1);
            }
        }
    }

    while (!s.empty()) {
        auto temp = s.top();
        s.pop();

        pair<int, int> currPos = temp.first;
        int currSet = temp.second;

        if (!visited[currPos.first][currPos.second]) {
            if (currSet == -1) {
                currSet = pointCounts.size();
                pointCounts.emplace_back(0);
            }

            visited[currPos.first][currPos.second] = true;
            setNum[currPos.first][currPos.second] = currSet;
            pointCounts[currSet]++;

            for (int dir=0; dir<4; dir++) {
                pair<int, int> next = make_pair(currPos.first + dy[dir], currPos.second + dx[dir]);

                if (isInRange(next, N, M) && !grid[next.first][next.second]) {
                    s.emplace(next, currSet);
                }
            }
        }
    }

    for (int i=0; i<N; i++) {
        for (int j=0; j<M; j++) {
            if (grid[i][j]) {
                int reachable = 1;
                vector<bool> checked(pointCounts.size(), false);

                for (int dir=0; dir<4; dir++) {
                    pair<int, int> adj = make_pair(i + dy[dir], j + dx[dir]);

                    if (isInRange(adj, N, M) && !grid[adj.first][adj.second]) {
                        int currSet = setNum[adj.first][adj.second];

                        if (!checked[currSet]) {
                            reachable += pointCounts[currSet];
                            checked[currSet] = true;
                        }
                    }
                }

                cout << reachable % 10;
            }
            else {
                cout << 0;
            }
        }
        cout << "\n";
    }

    return 0;
}

bool isInRange(const pair<int, int>& pos, int N, int M) {
    if (pos.first < 0 || pos.first > N - 1) {
        return false;
    }
    else if (pos.second < 0 || pos.second > M - 1) {
        return false;
    }
    else {
        return true;
    }
}

그러자 모든 테스트 케이스를 통과하고 ‘맞았습니다’가 나오는 것을 확인할 수 있었다.

마무리

탐색 문제는 이제 꽤 능숙해진 것 같다. 어떻게 짜야할지 생각이 안나도 필요한 코드를 저절로 치고 있는 스스로의 모습을 발견할 수 있었던 것이다!

다른 문제들도 이렇게 술술 풀 수 있었으면 좋겠다… 예를 들면 DP 라던가, DP 라던가, DP 라던가… 물론 그러려면 더 연습을 해야할 것 같긴 한다.

오늘의 PS는 여기까지!


1: https://www.acmicpc.net/problem/16946